home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 12 / Cream of the Crop 12 (Part II) / Cream of the Crop 12 (Part II).iso / OS2 / BLT2_205.ZIP / src / blt2cx01.c next >
Encoding:
C/C++ Source or Header  |  1996-02-25  |  15.6 KB  |  543 lines

  1. /* 
  2.  *
  3.  * blt2cx01.c - 17-Oct-1995 Cornel Huth 
  4.  * This module is called by blt2demo.c
  5.  * REINDEX_XB
  6.  * 25-Feb-96:
  7.  */
  8.  
  9. #include "platform.h"
  10.  
  11. #ifdef ON_OS2
  12.    #define INCL_DOSPROCESS // for OS/2 threads
  13.    #include <os2.h>
  14. #endif
  15. #ifdef ON_W95
  16.    #define WIN32_LEAN_AND_MEAN
  17.    #include <windows.h>
  18. #endif
  19.  
  20. #include <stdio.h>
  21. #include <stdlib.h>
  22. #include <time.h>
  23. #include <string.h>
  24.  
  25. #ifdef ON_OS2
  26.    #include "bullet2.h"
  27. #endif
  28. #ifdef ON_W95
  29.    #include "bullet95.h"
  30. #endif
  31. #ifdef ON_DOSX
  32.    #include "bulletx.h"
  33. #endif
  34.  
  35.  
  36. void cx01BuildFieldList(FIELDDESCTYPE fieldList[]);
  37.  
  38.  
  39. #ifdef ON_OS2
  40.    LONG reindexRez=0;   // thread reindex return code
  41.    TID tidReindex;      // reindex thread ID
  42.    VOID APIENTRY ReindexThread(ACCESSPACK *AP);
  43. #endif
  44. #ifdef ON_W95
  45.    LONG reindexRez=0;   // thread reindex return code
  46.    DWORD tidReindex;    // reindex thread ID
  47.    HANDLE hReindex;     // reindex thread's handle
  48.    VOID APIENTRY ReindexThread(ACCESSPACK *AP);
  49. #endif
  50.  
  51. extern CHAR *collateTable;
  52.  
  53.  
  54. int cx01(void) {
  55.  
  56. #pragma pack(1)
  57.  
  58. ACCESSPACK AP;
  59. DOSFILEPACK DFP;
  60. CREATEDATAPACK CDP;
  61. CREATEINDEXPACK CIP;
  62. HANDLEPACK HP;
  63. OPENPACK OP;
  64. QUERYSETPACK QSP;
  65.  
  66. #if defined ON_OS2 || defined ON_W95
  67.    STATINDEXPACK SIP;   // for reindex percentage done, needs thread support
  68. #endif
  69.  
  70. struct EmpRecType {
  71.  CHAR tag;              // record tag, init to SPACE, * means deleted
  72.  CHAR empID[9];         // SSN (not 0T string)
  73.  CHAR empLN[16];        // last name
  74.  CHAR empFN[16];        // first name
  75.  CHAR empHire[8];       // "YYYYMMDD" (not 0T string)
  76.  CHAR empDept[6];       // department assigned
  77. }; // 56 bytes
  78. struct EmpRecType EmpRec;
  79.  
  80. #pragma pack()
  81.  
  82. time_t startTime, endTime;
  83. int display = 0;                // display results or not flag
  84. int thread = 1;                 // dispatch reindex in a thread flag
  85. int lastNameVar = 0;            // used to construct on-the-fly data record...
  86. int lastFourVar = 0;            // ...that is unique
  87.  
  88. LONG rez;                       // return value from Bullet
  89.  
  90. CHAR nameIX3[] = "$CX01.IX3";   // name of index file created
  91. ULONG indexID=0;                // handle of index file
  92. CHAR keyExpression[128];        // key expression string buffer (159 max)
  93. CHAR keyBuffer[68];             // buffer used to store/receive key values
  94.  
  95. CHAR nameData[] = "$CX01.DBF";  // name of data file created
  96. ULONG dataID=0;                 // handle of data file
  97. FIELDDESCTYPE fieldList[5];     // 5 fields used in data record
  98.  
  99. LONG recs2add;          // records to add en-masse
  100. LONG i;                 // counter
  101. CHAR tmpStr[64];        // misc stuff, non-Bullet related
  102.  
  103. #define NEW_XBUFF_SIZE (384*1024)  // use 384KB for reindex buffer workspace
  104.  
  105.  
  106. memset(fieldList,0,sizeof(fieldList));  // init unused bytes to 0 (required)
  107. cx01BuildFieldList(fieldList);
  108.  
  109.  
  110. // Use 384K for workspace in reindex module
  111.  
  112. QSP.func = SET_SYSVARS_XB;
  113. QSP.item = REINDEX_BUFFER_SIZE;
  114. QSP.itemValue = NEW_XBUFF_SIZE;
  115. rez = BULLET(&QSP);
  116. if (rez) {
  117.    printf("Failed SET_SYSVARS call.  Err: %li\n",rez);
  118.    goto Abend;
  119. }
  120. printf("Bullet reindex tmp buffer was %ld KB, now %ld KB (0 KB means default, 144KB)\n",
  121.         QSP.itemValue/1024,NEW_XBUFF_SIZE/1024);
  122.  
  123. // Delete previous files from any previous run (disregard any error return)
  124.  
  125. DFP.func = DELETE_FILE_DOS;
  126. DFP.filenamePtr = nameData;
  127. rez = BULLET(&DFP);
  128. DFP.filenamePtr = nameIX3;
  129. rez = BULLET(&DFP);
  130.  
  131.  
  132. // Create the data file, a standard DBF (ID=3) as defined in fieldList above.
  133.  
  134. CDP.func = CREATE_DATA_XB;
  135. CDP.filenamePtr = nameData;
  136. CDP.noFields = 5;
  137. CDP.fieldListPtr = fieldList;
  138. CDP.fileID = 0x03;
  139. rez = BULLET(&CDP);
  140. if (rez) {
  141.    printf("Failed data file create.  Err: %li\n",rez);
  142.    goto Abend;
  143. }
  144.  
  145.  
  146. // Open the data file (required before creating an index file for it)
  147.  
  148. OP.func = OPEN_DATA_XB;
  149. OP.filenamePtr = nameData;
  150. OP.asMode = READWRITE | DENYNONE;
  151. rez = BULLET(&OP);
  152. if (rez) {
  153.    printf("Failed data file open.  Err: %li\n",rez);
  154.    goto Abend;
  155. }
  156. dataID = OP.handle;
  157.  
  158.  
  159. // Create an index file for the data file opened above.
  160. // This example uses a multi-part key composed of parts of last name and SSN.
  161.  
  162. strcpy(keyExpression,"SUBSTR(LNAME,1,4)+SUBSTR(SSN,6,4)");
  163.  
  164. CIP.func = CREATE_INDEX_XB;
  165. CIP.filenamePtr = nameIX3;
  166. CIP.keyExpPtr = keyExpression;
  167. CIP.xbLink = dataID;            // the handle of the data file
  168. CIP.sortFunction = NLS_SORT;    // sort by NLS
  169. CIP.codePage = CODEPAGE;        // code page to use, or 0 for system default
  170. CIP.countryCode = CTRYCODE;     // country code...
  171. CIP.collatePtr = collateTable;  // (see #define INT2165xxNS above)
  172. CIP.nodeSize = 512;             // 512-byte node size (or 1024, 2048 bytes)
  173. rez = BULLET(&CIP);
  174. if (rez) {
  175.    printf("Failed index file create.  Err: %li\n",rez);
  176.    goto Abend;
  177. }
  178.  
  179.  
  180. // Open the index file (what we just created above).
  181. // As with the index-create, the index-open requires the handle of the data
  182. // file which this index file indexes.
  183.  
  184. OP.func = OPEN_INDEX_XB;
  185. OP.filenamePtr = nameIX3;
  186. OP.asMode = READWRITE | DENYNONE;
  187. OP.xbLink = dataID;
  188. rez = BULLET(&OP);
  189. if (rez) {
  190.    printf("Failed index file open.  Err: %li\n",rez);
  191.    goto Abend;
  192. }
  193. indexID = OP.handle;
  194.  
  195.  
  196. printf("Display all data accessed (slower results)? (y/N) ");
  197. gets(tmpStr);
  198. if (*tmpStr=='y') display = 1;
  199.  
  200. #if defined ON_OS2 || defined ON_W95
  201.    printf("  Dispatch REINDEX_XB in a separate thread? (Y/n) ");
  202.    gets(tmpStr);
  203.    if (*tmpStr=='n') thread = 0;
  204. #else
  205.    thread = 0;
  206. #endif
  207.  
  208. printf("  How many records do you want for this test run? ");
  209. gets(tmpStr);
  210. recs2add = atol(tmpStr);
  211. if (recs2add < 1) recs2add = 1;         // would you rather end the test?
  212. if (recs2add > 9999999) recs2add = 1;   // why wait around for 10M?  
  213.  
  214.  
  215. // Add the data records, which are created here, on-the-fly, varying enough
  216. // to make unique records.
  217.  
  218. EmpRec.tag = ' ';                       // set to not-deleted
  219. strncpy(EmpRec.empID,"465990000",9);    // only changing last 4 in test
  220. strcpy(EmpRec.empLN,"0000LastNameNum"); // only changing first 4 in test
  221. strcpy(EmpRec.empFN,"YourFirstName");   // everyone has this first name!
  222. strncpy(EmpRec.empHire,"19950527",8);   // YYYYMMDD DBF form, no \0 on date
  223. strcpy(EmpRec.empDept,"MIS");           // everyone works for MIS!
  224.  
  225. printf("    Adding %ld records...  ",recs2add);
  226. time(&startTime);
  227.  
  228. AP.func = ADD_RECORD_XB;
  229. AP.handle = dataID;
  230. AP.recPtr = &EmpRec;
  231. for (i = 1; i <= recs2add; i++) {
  232.  
  233.    sprintf(tmpStr,"%4.4i",lastFourVar++);
  234.    strncpy(EmpRec.empID+5,tmpStr,4);            // update last 4 of empID
  235.    rez = BULLET(&AP);                           // AP. already setup
  236.    if (rez) {
  237.       printf("Failed while adding record %ld.  Err: %li\n",i,rez);
  238.       goto Abend;
  239.    }
  240.    if (lastFourVar > 9999) {                    // changes every 10,000 adds
  241.       lastFourVar = 0;
  242.       sprintf(tmpStr,"%4.4i",++lastNameVar);
  243.       strncpy(EmpRec.empLN,tmpStr,4);           // update first 4 of empLN
  244.    }
  245. }
  246. time(&endTime);
  247. printf("took %lu secs.\n",(endTime - startTime));
  248.  
  249.  
  250. // Reindex 
  251.  
  252. printf("Reindexing %ld records...  \r",recs2add);
  253. time(&startTime);
  254.  
  255. AP.func = REINDEX_XB;           // this is all there is to reindexing even...
  256. AP.handle = indexID;            // ...million-record databases
  257. AP.keyPtr = keyBuffer;          // if dup key error...(see manual for details)
  258. AP.nextPtr = NULL;              // just this one index file
  259.  
  260. if (thread) {
  261.  
  262. #ifdef ON_OS2
  263.  
  264.    rez = DosCreateThread(&tidReindex,
  265.                          (PFNTHREAD) &ReindexThread,
  266.                          (ULONG) &AP,
  267.                          CREATE_READY | STACK_SPARSE,
  268.                          32768);  // can get by with much less stack, even 8KB
  269.    if (rez) {
  270.       printf("Could not start reindex thread.  Err: %lu\n",rez);
  271.       goto Abend;
  272.    }
  273.  
  274.    DosSleep(1);  // wait a bit to let reindex code set progress to non-zero
  275.  
  276.    SIP.func = STAT_INDEX_XB;
  277.    SIP.handle = indexID;
  278.    rez = BULLET(&SIP);
  279.    while (rez==0) {
  280.       DosSleep(100);
  281.       printf("Reindexing %ld records... %3.3lu%%\r",recs2add,SIP.progress);
  282.       rez = BULLET(&SIP);
  283.       if (SIP.progress==0) {
  284.          printf("Reindexing %ld records...  ",recs2add);
  285.          break;
  286.       }
  287.    }
  288.    if (rez)
  289.       printf("\nFailed progress check.  Err: %li\n",rez);
  290.  
  291.    // Can actually get here _before_ the reindex thread fully exits, but
  292.    // won't get here until SIP.progress is back to 0.
  293.  
  294.    time(&endTime);
  295.  
  296.    // It's likely that the thread has exited completely by now, but if
  297.    // it was blocked after setting SIP.progress to 0 (hung up in the cache,
  298.    // etc.), then it's possible to get here well before Bullet has exited.
  299.    // Since calling Bullet while Bullet is busy (for most routines) results
  300.    // in a rc=640 being returned (mutex timeout), just call this to wait
  301.    // for the thread to exit completely.  Note that this has only been seen
  302.    // under Win95 (done, but blocked), but this little routine won't hurt.
  303.    // Ignore any error, which there will be if the thread has indeed exited.
  304.    // Another option is to use a non-0 mutex-timeout value, via SET_SYSVARS_XB.
  305.  
  306.    rez = DosWaitThread(&tidReindex,DCWW_WAIT);
  307.    // ignore error (such as "invalid thread ID")
  308.  
  309.    DosSleep(1); // allow the reindex thread to exit BULLET() (and set rez)
  310.  
  311.    rez = reindexRez;       // get return code set by ReindexThread()
  312.    if (rez) {              // rez is already AP.stat
  313.       printf("Failed reindex.  Err: %li\n",rez);
  314.       goto Abend;
  315.    }
  316.    printf("took %lu secs.\n",(endTime - startTime));
  317.  
  318. #endif
  319. #ifdef ON_W95
  320.  
  321.    hReindex = CreateThread(NULL,
  322.                            32768,               // 8KB min.
  323.                            (LPTHREAD_START_ROUTINE) &ReindexThread,
  324.                            (PACCESSPACK) &AP,
  325.                            0,                   // immediate start
  326.                            &tidReindex);
  327.                       
  328.  
  329.    if (hReindex==0) {
  330.       rez = GetLastError();
  331.       printf("Could not start reindex thread.  Err: %lu\n",rez);
  332.       goto Abend;
  333.    }
  334.  
  335.    Sleep(32);   // wait a bit to let reindex code set progress to non-zero
  336.  
  337.    SIP.func = STAT_INDEX_XB;
  338.    SIP.handle = indexID;
  339.    rez = BULLET(&SIP);
  340.    while (rez==0) {
  341.       Sleep(100);
  342.       printf("Reindexing %ld records... %3.3lu%%\r",recs2add,SIP.progress);
  343.       rez = BULLET(&SIP);
  344.       if (SIP.progress==0) {
  345.          printf("Reindexing %ld records...  ",recs2add);
  346.          break;
  347.       }
  348.    }
  349.    if (rez)
  350.       printf("\nFailed progress check.  Err: %li\n",rez);
  351.  
  352.    // Can actually get here _before_ the reindex thread fully exits, but
  353.    // won't get here until SIP.progress is back to 0.
  354.  
  355.    time(&endTime);
  356.  
  357.    // It's likely that the thread has exited completely by now, but if
  358.    // it was blocked after setting SIP.progress to 0 (hung up in the cache,
  359.    // etc.), then it's possible to get here well before Bullet has exited.
  360.    // Since calling Bullet while Bullet is busy (for most routines) results
  361.    // in a rc=640 being returned (mutex timeout), just call this to wait
  362.    // for the thread to exit completely.  Note that this has only been seen
  363.    // under Win95 (done, but blocked), but this little routine won't hurt.
  364.    // Ignore any error, which there will be if the thread has indeed exited.
  365.    // Another option is to use a non-0 mutex-timeout value, via SET_SYSVARS_XB.
  366.    // Error code 640 is documented in BULLET95.H.
  367.  
  368.    rez = WaitForSingleObject(hReindex,10*1000); // 10 secs plenty of flush time
  369.    // ignore error
  370.  
  371.    Sleep(32);   // allow the reindex thread to exit BULLET() (and set rez)
  372.                 // why 32ms?  Only because OS/2 min std resolution is 32ms
  373.  
  374.    rez = reindexRez;       // get return code set by ReindexThread()
  375.    if (rez) {              // rez is already AP.stat
  376.       printf("Failed reindex.  Err: %li\n",rez);
  377.       goto Abend;
  378.    }
  379.    printf("took %lu secs.\n",(endTime - startTime));
  380.  
  381. #endif
  382. #ifdef ON_DOSX
  383.  
  384.    printf(" Threads are not supported on this OS\n");
  385.    goto Abend;
  386.  
  387. #endif
  388.  
  389. }
  390. else {
  391.    rez = BULLET(&AP);
  392.    if (rez)  {                  // rez is 1 or 0 (cannot be negative here)
  393.       rez = AP.stat; 
  394.       printf("Failed reindex.  Rez: %li  Err: %li\n",rez, AP.stat);
  395.       goto Abend;
  396.    }
  397.    time(&endTime);
  398.    printf("Reindexing %ld records...  took %lu secs.\n",
  399.            recs2add,
  400.            (endTime-startTime));
  401. }
  402.  
  403.  
  404. // Get key data
  405.  
  406. memset(keyBuffer,0,sizeof(keyBuffer)); 
  407. printf(" Accessing %ld keys...     ",recs2add);
  408. if (display) printf("\n");
  409. time(&startTime);
  410. AP.func = FIRST_KEY_XB;
  411. AP.handle = indexID;
  412. AP.keyPtr = keyBuffer;
  413. rez = BULLET(&AP);
  414. i=0;
  415. while (rez==0) {
  416.    if (display) printf("%s  %9.9lu\r", keyBuffer, AP.recNo);
  417.    i++;     
  418.    AP.func = NEXT_KEY_XB;
  419.    rez = BULLET(&AP);
  420. };
  421. if (display) printf("\n...");
  422.  
  423. // expected rez is EXB_END_OF_FILE
  424.  
  425. if (rez!=EXB_END_OF_FILE)  {
  426.    printf("Failed KEY access.  Err: %li\n",rez);
  427.    goto Abend;
  428. }
  429. time(&endTime);
  430. printf("took %lu secs. for %ld keys\n",(endTime - startTime),i);
  431.  
  432.  
  433. // Get key and record data
  434.  
  435. printf(" Accessing %ld keys+recs...",recs2add);
  436. if (display) printf("\n");
  437. time(&startTime);
  438. AP.func = GET_FIRST_XB;
  439. AP.handle = indexID;
  440. AP.keyPtr = keyBuffer;
  441. AP.recPtr = &EmpRec;
  442. rez = BULLET(&AP);
  443. i=0;
  444. while (rez==0) {
  445.    if (display) 
  446.       printf("%s  %9.9lu   %s\r", keyBuffer, AP.recNo, &EmpRec); // partial show
  447.    i++;
  448.    AP.func = GET_NEXT_XB;
  449.    rez = BULLET(&AP);
  450. };
  451. if (display) printf("\n...");
  452.  
  453. // expected rez is EXB_END_OF_FILE
  454.  
  455. if (rez!=EXB_END_OF_FILE) {
  456.    printf("Failed GET access.  Err: %li\n",rez);
  457.    goto Abend;
  458. }
  459. time(&endTime);
  460. printf("took %lu secs. for %ld keys & records\n",(endTime - startTime),i);
  461.  
  462.  
  463. // Fatal errors above come straight to here
  464. Abend:
  465.  
  466. // Close files
  467.  
  468. if (indexID) {
  469.    HP.func = CLOSE_INDEX_XB;
  470.    HP.handle = indexID;
  471.    rez = BULLET(&HP);
  472.    if (rez)
  473.       printf("Failed index file close.  Err: %li\n",rez);
  474. }
  475.  
  476. // Unlikely the above could fail, considering how far it has gotten so far!
  477. // But logic says that we want to continue closing other open files...
  478.  
  479. if (dataID) {
  480.    HP.func = CLOSE_DATA_XB;
  481.    HP.handle = dataID;
  482.    rez = BULLET(&HP);
  483.    if (rez)
  484.       printf("Failed data file close.  Err: %li\n",rez);
  485. }
  486.  
  487. return rez;  // module exit
  488. }
  489.  
  490.  
  491. #if defined ON_OS2 || defined ON_W95
  492.  
  493. // -------------------------
  494. // Reindex thread, thread #2
  495.  
  496. void APIENTRY ReindexThread(ACCESSPACK *AP) {
  497.  
  498.    reindexRez = BULLET(AP);
  499.  
  500.    // Reindex is a xaction-list routine and so must check AP.stat for any 
  501.    // error code -- rez as returned from the Bullet call is the list item 
  502.    // that failed.  Since this example has but the single item, rez=1 on 
  503.    // failure, with the error code in AP.stat.
  504.  
  505.    if (reindexRez) {
  506.       reindexRez = AP->stat;
  507.    }
  508. }
  509. #endif
  510.  
  511.  
  512. //------------------------------------
  513. // Init field list items for data file
  514.  
  515. void cx01BuildFieldList(FIELDDESCTYPE fieldList[]) {
  516.  
  517. strcpy(fieldList[0].fieldName, "SSN");  // field names must be upper-case
  518. fieldList[0].fieldType = 'C';           // field types must be upper-case
  519. fieldList[0].fieldLen = 9;
  520. fieldList[0].fieldDC = 0;
  521.  
  522. strcpy(fieldList[1].fieldName, "LNAME");
  523. fieldList[1].fieldType = 'C';
  524. fieldList[1].fieldLen = 16;
  525. fieldList[1].fieldDC = 0;
  526.  
  527. strcpy(fieldList[2].fieldName, "FNAME");
  528. fieldList[2].fieldType = 'C';
  529. fieldList[2].fieldLen = 16;
  530. fieldList[2].fieldDC = 0;
  531.  
  532. strcpy(fieldList[3].fieldName, "HIRED");
  533. fieldList[3].fieldType = 'D';
  534. fieldList[3].fieldLen = 8;      // date field type must be 8.0
  535. fieldList[3].fieldDC = 0;
  536.  
  537. strcpy(fieldList[4].fieldName, "DEPT");
  538. fieldList[4].fieldType = 'C';
  539. fieldList[4].fieldLen = 6;
  540. fieldList[4].fieldDC = 0;
  541. }
  542.  
  543.